package server

import (
	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
	"github.com/gorilla/websocket"
	"github.com/panjf2000/ants/v2"
	"github.com/panjf2000/gnet/v2"
	"net/http"
	"time"
	"wths/wthsjy-im/plugins/log"
	"wths/wthsjy-im/protocol"
	"wths/wthsjy-im/server/client"
)

func (ims *ImServer) OnTick() (delay time.Duration, action gnet.Action) {
	log.Info("OnTick").Msgf("connNum:[%d]", ims.connNum.Load())
	return time.Second * 30, gnet.None
}

func (ims *ImServer) OnBoot(eng gnet.Engine) gnet.Action {
	ims.eng = eng
	return gnet.None
}

func (ims *ImServer) OnOpen(c gnet.Conn) (out []byte, action gnet.Action) {
	ims.connNum.Inc()
	return
}
func (ims *ImServer) OnClose(c gnet.Conn, err error) (action gnet.Action) {
	ims.connNum.Dec()

	if cli, ok := c.Context().(*client.Client); ok {
		cli.Close()
		log.Debug("###OnClose").Msgf("%s,%p,%v", cli.ClientID(), c, err)
	} else {
		log.Debug("###OnClose").Msgf("不存在ctx,%p,%v", c, err)
	}

	return gnet.None
}

func (ims *ImServer) OnTraffic(c gnet.Conn) gnet.Action {
	cli, ok := c.Context().(*client.Client)
	if !ok {
		cli = client.NewClient(c, ims.conf.HandlerFunc, ims.topicMgr)
		c.SetContext(cli)

		upgraded, err := ims.tryUpgradeWs(c)
		if err != nil {
			return gnet.Close
		}
		if upgraded {
			cli.IsWebsocket = true
			return gnet.None
		}
	}

	if cli.IsWebsocket {
		return ims.processWebsocket(c, cli)
	} else {
		return ims.processUnPack(c, cli)
	}

}

// 尝试升级为websocket
func (ims *ImServer) tryUpgradeWs(c gnet.Conn) (upgradeSuccess bool, err error) {
	bs, _ := c.Peek(3)
	if string(bs) != http.MethodGet {
		return false, nil
	}
	_, err = ws.Upgrade(c)

	return err == nil, err
}

// tcp 分包
func (ims *ImServer) processUnPack(c gnet.Conn, cli *client.Client) gnet.Action {
	for {
		p, err := protocol.ReadGnet(c)
		if err == protocol.ErrIncompletePacket {
			return gnet.None
		}
		if err != nil {
			log.Error("processUnPack").Msg(err)
			return gnet.Close
		}

		if err = ants.Submit(func() {
			cli.HandleProtocol(p)
		}); err != nil {
			log.Error("processUnPack").Msg(err)
			return gnet.Close
		}
	}
}

// websocket 解包
func (ims *ImServer) processWebsocket(c gnet.Conn, cli *client.Client) gnet.Action {
	bs, op, err := wsutil.ReadClientData(c)
	if op != websocket.BinaryMessage {
		return gnet.Close
	}
	if err != nil {
		if _, ok := err.(wsutil.ClosedError); !ok {
			log.Error("processWebsocket").Msg(err)
		}
		return gnet.Close
	}

	p, err := protocol.Decode(bs)
	if err != nil {
		log.Error("processWebsocket").Msg(err)
		return gnet.Close
	}

	if err = ants.Submit(func() {
		cli.HandleProtocol(p)
	}); err != nil {
		log.Error("processWebsocket").Msg(err)
		return gnet.Close
	}

	return gnet.None
}
